package cn.caplike.demo.nio._2017;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;

/**
 * 简介：FileChannel
 *
 * @author 李珂 2017年11月22日 下午1:41:16
 */
public class FileChannelTest {

    private static Path filePath;
    private static Path inPath;
    private static Path outPath;

    static {
        filePath = Paths.get("data/file-channel");
        inPath = Paths.get("data/file-channel/in.txt");
        outPath = Paths.get("data/file-channel/out.txt");
        try {
            if (!Files.exists(filePath)) {
                Files.createDirectory(filePath);
            }
            if (!Files.exists(inPath)) {
                Files.createFile(inPath);
            }
            if (!Files.exists(outPath)) {
                Files.createFile(outPath);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void run1() throws IOException {
        /*
         * 1.	Channel是需要关闭的，所以这里用TWR方式确保Channel正确关闭；
         * 2.	鼓励用这种方法打开通道FileChannel.open(Path path, OpenOption... options)；
         */
        try (
                FileChannel inChannel = FileChannel.open(inPath, StandardOpenOption.READ);
                FileChannel outChannel = FileChannel.open(outPath, StandardOpenOption.WRITE)
        ) {
            ByteBuffer buf = ByteBuffer.allocate(48);
            /*
             * 1.	channel.write()和read()方法是需要移动position和limit指针的，所以需要用buffer.flip()等方法，来保证读写正确；
             * 2.	channel.read()方法是从通道读取到缓冲区中，读取的字节数量是n（n是buffer中当前剩余的容量），但是读取的数量是取决于通道的当前状态。
             * 	例如：要读到文件末尾，不够buffer的容量也就是通道剩余<=n,
             * 	或者说ServerSocketChannel当前只能读取准备好的，这很可能<n，所以说加循环，
             * 	另外read的方法返回当前读取的数量，一个int。
             * 	可以根据他来设定while：如果返回-1，表示到了文件末尾。
             */
            int bytesRead = inChannel.read(buf);
            while (bytesRead != -1) {
                buf.flip();
                /*
                 *	注意fileChannel.write()是在while循环中调用的。
                 *	因为无法保证write()方法一次能向FileChannel写入多少字节，
                 *	因此需要重复调用write()方法，直到Buffer中已经没有尚未写入通道的字节。
                 */
                while (buf.hasRemaining()) {
                    outChannel.write(buf);
                }
                buf.clear();
                bytesRead = inChannel.read(buf);
            }
        }
    }

    public static void run2() throws IOException {
        /*
         *	如果只是将一个数据源通过FileChannel，转移到另一个数据源，还有一种更加简单的方法
         */
        try (
                FileChannel inChannel = FileChannel.open(inPath, StandardOpenOption.READ);
                FileChannel outChannel = FileChannel.open(outPath, StandardOpenOption.WRITE)
        ) {
            outChannel.transferFrom(inChannel, 0, inChannel.size());
            inChannel.transferTo(0, inChannel.size(), outChannel);
        }
    }

}
